%{
// Simple Logo - Analisador Sintático e Semântico

// Bibliotecas necessárias
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

// Tratamento de erros
int errorline = 1;

void yyerror(const char *str)
{
	printf("Erro na análise sintática próximo à linha %d\n", errorline);
	exit(0);
}
 
int yywrap()
{
        return 1;
}

// Arquivo onde será código intermediário
FILE *rubyfile;

%}

// Identificadores da linguagem
%union {
	char id[1024];
}

// Tokens
%token if_ repeat_ for_ wait_ penup pendown turnleft turnright direction go gox goy reset pencolor penwidth canvassize canvascolor intnum variable while_ openpar closepar openbracket closebracket commas to_ true_ false_ greater less equal more minus_ mult division and_ or_ forward backward write_ text_
%%

// O programa é uma sequência de comandos, definidos em seguida
// No finak imprimimos o rodapé do código intermediário, comum a todos
programa:
	cmd {errorline++;} recursivecmd {
	fprintf(rubyfile, "    end\n\nend\n\nGtk.init\nwindow = Compiler.new\nGtk.main"); fclose(rubyfile);}
	;

recursivecmd:/* Empty */
	| cmd {errorline++;} recursivecmd	
	;

codeblock:
	recursivecmd
	;

// Cláusula If: Executa o código entre chaves se a condição entre parênteses for verdadeira
cmdif:
	if_ openpar boolexp closepar openbracket {fprintf(rubyfile, "if (%s)\n", $<id>3);} codeblock closebracket {fprintf(rubyfile, "end\n");}
	;

// Repeat x vezes o comando entre chaves
// O número de vezes deve ser maior que 0
cmdrepeat:
	repeat_ matexp {
			if (atoi($<id>2) < 0) printf("Warning: Parâmetro para comando repeat deve ser maior que 0\n");
		}
	 	openbracket {
			if (atoi($<id>2) >= 0) fprintf(rubyfile, "(%s).repeat {\n", $<id>2);}
			codeblock closebracket { if (atoi($<id>2) >= 0) fprintf(rubyfile, "}\n");
		}	
	;

// Repete o laço x vezes, onde x é o intervalo de valores assumidos pela variável
cmdfor:
	for_ matattr to_ matexp openbracket {
		char pvar[30];
		char pexp[30];
		char* result;
		result = strtok($<id>2, "?");
		strcpy(pvar, result);			
		result = strtok(NULL, "?");
		strcpy(pexp, result);
		fprintf(rubyfile, "for %s in %s..%s\n",pvar, pexp, $<id>4); 
	}
	codeblock closebracket {fprintf(rubyfile, "end\n");}
	;

// Espera x segundos sem executar ação alguma, onde x é uma expressão matemática
cmdwait:
	wait_ matexp {
		fprintf(rubyfile,"wait(%s)\n", $<id>2);
	}
	;

// Enquanto a condição entre parênteses for verdadeira, executa o código entre chaves
cmdwhile:
	while_ openpar boolexp closepar openbracket{fprintf(rubyfile, "while (%s) do\n",$<id>3);} 
	codeblock closebracket {
		fprintf(rubyfile, "end\n");
	}
	;

// Apenas gera o código penup equivalente em Ruby
cmdpenup:
	penup {fprintf(rubyfile, "penup\n");}
	;

// Apenas gera o código pendown equivalente em Ruby
cmdpendown:
	pendown {fprintf(rubyfile, "pendown\n");}
	;

// Vira à esquerda. O valor de ângulo deve estar entre 0 e 360
cmdturnleft:
	turnleft matexp {
		if ((atoi($<id>2) > 360)||(atoi($<id>2) < 0))
			printf("Warning: Ângulo inválido para turnleft\n");
         	else {
			fprintf(rubyfile, "turnleft(%s.to_i)\n", $<id>2);
		}
	}
	;

// Vira à direita. O valor de ângulo deve estar entre 0 e 360
cmdturnright:
	turnright matexp {
		if ((atoi($<id>2) > 360)||(atoi($<id>2) < 0))
			printf("Warning: Ângulo inválido para turnright\n");
	        else {
			fprintf(rubyfile, "turnright(%s.to_i)\n", $<id>2);
		}
	} 
	;

// Altera a direção, ou seja, o ângulo. O valor de ângulo deve estar entre 0 e 360
cmddirection:
	direction matexp {
		if ((atoi($<id>2) > 360)||(atoi($<id>2) < 0))
			printf("Warning: Ângulo inválido para direction\n");
	        else {
			fprintf(rubyfile, "direction(%s.to_i)\n", $<id>2);
		}
	} 
	;

// Gera o comando Go, que move o GNU para determinada posição
cmdgo:
	go matexp commas matexp {fprintf(rubyfile, "go %s, %s\n", $<id>2, $<id>4);}
	;

// Gera o comando Gox, que move o GNU para determinada posição x
cmdgox:
	gox matexp {fprintf(rubyfile, "gox %s\n", $<id>2);}
	;

// Gera o comando Goy, que move o GNU para determinada posição y
cmdgoy:
	goy matexp {fprintf(rubyfile, "goy %s\n", $<id>2);}
	;

// Gera o comando reset, que apaga toda a tela e reverte os parâmetros aos seus valores iniciais
cmdreset:
	reset {fprintf(rubyfile, "reset\n");}
	;

// Altera a cor da caneta. Os valores de RGB devem estar cada um numa faixa de 0 a 65535
cmdpencolor:
	pencolor matexp commas matexp commas matexp {
		if ((atoi($<id>2) > 65535 )||( atoi($<id>4) > 65535 )||( atoi($<id>6) > 65535 )||( atoi($<id>2) < 0 )||( atoi($<id>4) < 0 )||( atoi($<id>6) < 0))
			printf("Warning: Valores inválidos para cor\n");
		else
			fprintf(rubyfile, "pencolor %s, %s, %s\n", $<id>2, $<id>4, $<id>6);
	}
	;

// Gera o comando penwidth, que altera a espessura da caneta
cmdpenwidth:
	penwidth matexp {fprintf(rubyfile, "penwidth(%s.to_i)\n", $<id>2);}
	;

// Gera o comando canvassize, que altera o tamanho da tela
cmdcanvassize:
	canvassize matexp commas matexp {fprintf(rubyfile, "canvassize %s",$<id>2);}
	;

// Altera a cor de fundo da tela. Os valores de RGB devem estar cada um numa faixa de 0 a 65535
cmdcanvascolor:
	canvascolor matexp commas matexp commas matexp {
		if ((atoi($<id>2) > 65535 )||( atoi($<id>4) > 65535 )||( atoi($<id>6) > 65535 )||( atoi($<id>2) < 0 )||( atoi($<id>4) < 0 )||( atoi($<id>6) < 0))
			printf("Warning: Valores inválidos para cor\n");
		else
			fprintf(rubyfile, "canvascolor %s, %s, %s", $<id>2, $<id>3, $<id>4);
	}
	;

// Gera o comando forward, que faz o GNU andar para frente
cmdforward:
	forward matexp {fprintf(rubyfile, "forward(%s.to_i)\n", $<id>2);}
	;

// Gera o comando backward, que faz o GNU andar para trás
cmdbackward:
	backward matexp {fprintf(rubyfile, "backward(%s.to_i)\n", $<id>2);}
	;

// Gera o comando showtext, que imprime um texto na tela no posição atual com a cor atual
cmdwrite:
        write_ text_ {
        	fprintf(rubyfile, "showtext(%s)\n", $<id>2);
        }
        ;

// Calcula um valor inteiro
intnumvalue:
	variable {strcpy($<id>$,($<id>1+1));}
	|intnum {strcpy($<id>$,$<id>1);}
	|minus_ intnum { strcpy($<id>$, ""); strcat($<id>$, "-"); strcat($<id>$, $<id>2); }
	;

// Valor booleano (verdadeiro ou falso)
boolvalue:
	intnumvalue {strcpy($<id>$,$<id>1);}
	|true_ {strcpy($<id>$,$<id>1);}
	|false_ {strcpy($<id>$,$<id>1);}
	;

// Todos os comandos possíveis
cmd:
	matattr
	|cmdif
	|cmdrepeat
	|cmdfor
	|cmdwait
	|cmdwhile
	|cmdpenup
	|cmdpendown
	|cmdturnleft
	|cmdturnright
	|cmddirection   
	|cmdgo
	|cmdgox
	|cmdgoy
	|cmdreset 
	|cmdpencolor
	|cmdpenwidth
	|cmdcanvassize
	|cmdcanvascolor
	|cmdforward
	|cmdbackward
	|cmdwrite
	;		

// Expressão matemática
matexp:
	matexp more expm2 {strcat($<id>1, "+"); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);}
        | matexp minus_ expm2 {strcat($<id>1, "-"); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);}
	| expm2 {strcpy($<id>$,$<id>1);}
	;

expm2:
	expm2 mult expm3 {strcat($<id>1, "*"); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);}
	| expm2 division expm3 {strcat($<id>1, "/"); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);}
	| expm3 {strcpy($<id>$,$<id>1);}
	;

expm3:
	openpar matexp closepar {strcat($<id>2, ")"); strcpy($<id>$, "("); strcat($<id>$,$<id>2);}
	| intnumvalue {strcpy($<id>$,$<id>1); }
	;

// Atribuição matemática
matattr:
	variable equal matexp{
	fprintf(rubyfile, "%s = %s\n",($<id>1+1), $<id>3);
	strcpy($<id>$,($<id>1+1));
	strcat($<id>$,"?");
	strcat($<id>$, $<id>3);
	strcat($<id>$,"?");
	}
        ;

// Expressão booleana
boolexp:
	boolexp equal equal expb2{strcat($<id>1, "=="); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);} 
	| boolexp and_ expb2{strcat($<id>1, " and "); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);} 
	| boolexp or_ expb2 {strcat($<id>1, " or "); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);} 
	| expb2 {strcpy($<id>$,$<id>1);}
	;

expb2:
	expb2 greater expb3 {strcat($<id>1, ">"); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);}
	|expb2 less expb3{strcat($<id>1, "<"); strcat($<id>1, $<id>3); strcpy($<id>$,$<id>1);}
	|expb3{strcpy($<id>$,$<id>1);}
	;

expb3:
	openpar boolexp closepar{strcat($<id>2, ")"); strcpy($<id>$, "("); strcat($<id>$,$<id>2);}
	| boolvalue {strcpy($<id>$,$<id>1);}
	;

%%
#include <stdio.h>

int main( )
{
	// Abre o arquivo onde será gerado o código intermediário
	rubyfile = fopen("intermediario.rb", "w");

	// Imprime o cabeçalho do arquivo, comum a todos
	// Destaque para o require 'lib', que é o arquivo onde estão as definições das funções do Simple Logo
	fprintf(rubyfile,"#!/usr/bin/ruby\n\nrequire 'lib'\n\nclass Compiler < Gtk::Window\n\ndef run_cmds\n\n");

	// Realiza a análise da fato
 	return yyparse();
}
